{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0ebfaad0",
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline \n",
    "import random\n",
    "import torch\n",
    "from d2l import torch as d2l"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6430da96",
   "metadata": {},
   "source": [
    "![](https://cdn.zengchen233.cn/img/202309170925263.PNG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "47a8d9ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "# %matplotlib inline 可以在Ipython编译器里直接使用，功能是可以内嵌绘图，并且可以省略掉plt.show()这一步。\n",
    "def synthetic_data(w, b, num_examples):\n",
    "    '''生成 y=wx+b+噪声'''\n",
    "    X = torch.normal(0, 1, (num_examples, len(w))) # 均值为0，标准差为1\n",
    "    y = torch.matmul(X, w) + b\n",
    "    y += torch.normal(0, 0.01, y.shape)\n",
    "    return X, y.reshape((-1, 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "636352ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "true_w = torch.tensor([2, -3.4])\n",
    "true_b = 4.2\n",
    "features, labels = synthetic_data(true_w, true_b, 1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "fafa0700",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "features: tensor([-0.6150, -2.2974]) \n",
      "labels: tensor([10.7872])\n",
      "1000\n",
      "1000\n"
     ]
    }
   ],
   "source": [
    "print('features:', features[0], '\\nlabels:',labels[0])\n",
    "print(len(features))\n",
    "print(len(labels))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e9553401",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x105725be0>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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lt+8eXOCrUqepPzOnTuTbkZq/PKXv8xLyj/99NNJNyUVWltb8//0T/+UdDMqjhGRiJ08eVLf+MY39JGPfEQNDQ1JNycVBgcHNXv27KSbgQo5c+aMDh48qBUrVhS+N23aNK1YsULPPPNMgi1D2gwODkpSzf//w/j4uJ544gkNDw/rmmuuSbo5FUcgEpG/+Iu/UHNzsy644AK9+uqr2rVrV9JNSoWXX35ZX/nKV/SpT30q6aagQn71q19pfHxcc+fOLfr+3Llz9eabbybUKqTNxMSE7r77bl177bVauHBh0s1JxE9/+lO95z3vUWNjoz796U9r586d+uAHP5h0syqOQMTFvffeq1wu5/n10ksvFY7/sz/7Mx06dEh79+5VXV2dbr/9duWrqGht0PchScePH9cNN9ygW2+9VZ/85CcTann0wrwLAMXuvPNOHTlyRE888UTSTUnMggUL1NPTo2effVbr16/XunXr9LOf/SzpZlUcJd5dnDhxQm+//bbnMVdccYWmT58+5fuvv/665s+frx//+MdVM8wW9H309/dr+fLlWrZsmR5//HFNm1Y9MW+YfxuPP/647r77bg0MDMTcuuSdOXNG559/vv7t3/5Na9asKXx/3bp1GhgYqOnRwlwup507dxa9l1r0mc98Rrt27dKBAwd0+eWXJ92c1FixYoU6Ojr01a9+NemmVFR90g1Iqzlz5mjOnDmhzp2YmJAkjY6ORtmkRAV5H8ePH9d1112nzs5OPfbYY1UVhEjl/duoBdOnT1dnZ6f27dtX6HAnJia0b98+feYzn0m2cUhUPp/XZz/7We3cuVP79+8nCLGZmJioqn7DLwKRMj377LP6n//5H330ox9Va2urent79fnPf14dHR1VMxoSxPHjx7V8+XK1t7dr69atOnHiROFn8+bNS7BlyXj11Vd18uRJvfrqqxofH1dPT48k6X3ve5/e8573JNu4GN1zzz1at26dfvM3f1NXXXWVHnroIQ0PD+uOO+5IumkV98477+jll18u/P3YsWPq6enR7NmzdemllybYssq788479c1vflO7du3SjBkzCjlDs2bNUlNTU8Ktq6yNGzfqxhtv1KWXXqrTp0/rm9/8pvbv3689e/Yk3bTKS3bRTvYdPnw4f9111+Vnz56db2xszF922WX5T3/60/nXX3896aYl4rHHHstLcvyqRevWrXN8Fz/4wQ+SblrsvvKVr+QvvfTS/PTp0/NXXXVVvru7O+kmJeIHP/iB47+BdevWJd20inP7/4bHHnss6aZV3J/8yZ/k29vb89OnT8/PmTMn//GPfzy/d+/epJuVCHJEAABAYqpr8h4AAGQKgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEgMgQgAAEjM/wd7PIspstWsUwAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "d2l.set_figsize\n",
    "d2l.plt.scatter(features[:, 1].detach().numpy(),\n",
    "               labels.detach().numpy(), 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6b1164b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "def data_iter(batch_size, features, labels): # 该函数接收批量大小，特征矩阵和向量坐标作为输入，生成大小为batch_size的小批量\n",
    "    num_examples = len(features)\n",
    "    indices = list(range(num_examples))\n",
    "    random.shuffle(indices)\n",
    "    for i in range(0, num_examples, batch_size):\n",
    "        batch_index = torch.tensor(\n",
    "            indices[i: min(i + batch_size, num_examples)])\n",
    "        yield features[batch_index], labels[batch_index] # 使用yield的函数是有状态的，返回一个值后，还可以继续调用，直到所有yield都被用完"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4206c30d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0.3523,  1.3666],\n",
      "        [-0.6864, -0.3442],\n",
      "        [-1.6610, -0.2404],\n",
      "        [ 0.8846,  0.6332],\n",
      "        [-0.5617, -0.2399],\n",
      "        [-1.9018, -0.2738],\n",
      "        [ 1.5919, -0.2028],\n",
      "        [ 0.0294,  0.2552],\n",
      "        [ 1.3052, -0.4297],\n",
      "        [-0.3588, -1.1261]]) \n",
      " tensor([[0.2403],\n",
      "        [4.0052],\n",
      "        [1.6954],\n",
      "        [3.8130],\n",
      "        [3.9149],\n",
      "        [1.3302],\n",
      "        [8.0747],\n",
      "        [3.3810],\n",
      "        [8.2760],\n",
      "        [7.3097]])\n"
     ]
    }
   ],
   "source": [
    "batch_size = 10\n",
    "\n",
    "for X, y in data_iter(batch_size, features, labels):\n",
    "    print(X,'\\n',y)\n",
    "    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b5d55a9b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义初始化模型参数\n",
    "w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)\n",
    "b = torch.zeros(1, requires_grad=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "79c878c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def linreg(X, w, b):\n",
    "    return torch.matmul(X, w) + b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b98e53e4",
   "metadata": {},
   "source": [
    "<h3><center>$J(w,b) = \\frac{1}{2m} \\sum_{i=1}^{m}（\\hat{y}-y）^{2}$</center></h3>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "258b61bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def squared_loss(y_hat, y):\n",
    "    return (y_hat - y.reshape(y_hat.shape))**2 / 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "2638dc26",
   "metadata": {},
   "outputs": [],
   "source": [
    "def sgd(params, lr, batch_size):\n",
    "    '''小批量随机梯度下降'''\n",
    "    with torch.no_grad(): # 更新的时候不要参与梯度计算\n",
    "        for param in params:\n",
    "            param -= lr * param.grad / batch_size\n",
    "            param.grad.zero_()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f97108f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "lr = 0.03\n",
    "num_epochs = 3\n",
    "net = linreg\n",
    "loss = squared_loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "db463f0a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 1, loss 0.023871\n",
      "epoch 2, loss 0.000088\n",
      "epoch 3, loss 0.000049\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(num_epochs):\n",
    "    for X, y in data_iter(batch_size, features, labels):\n",
    "        l = loss(net(X, w, b), y)  # X和y的小批量损失, xy只是数据集当中的一个数\n",
    "        # 因为l形状是(batch_size,1)，而不是一个标量。l中的所有元素被加到一起，\n",
    "        # 并以此计算关于[w,b]的梯度\n",
    "        l.sum().backward()\n",
    "        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数\n",
    "    with torch.no_grad():\n",
    "        train_l = loss(net(features, w, b), labels)\n",
    "        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "bf535fc8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w的估计误差: tensor([-0.0002, -0.0003], grad_fn=<SubBackward0>)\n",
      "b的估计误差: tensor([0.0007], grad_fn=<RsubBackward1>)\n"
     ]
    }
   ],
   "source": [
    "print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')\n",
    "print(f'b的估计误差: {true_b - b}')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "d2l",
   "language": "python",
   "name": "d2l"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
